#ifndef HARDWAREINTERFACE_HPP
#define HARDWAREINTERFACE_HPP

#include <QObject>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>

#include "datamemoryorganisation.hpp"
#include "qserialdevice/abstractserial.h"
#include "appsettings.hpp"

class DataMemoryOrganisation;

/**
* @brief Class HardwareInterface providing access to the serial Port
* @details This Class provides abstract access to the Serial Port. \n
* The serial port can be accessed on Linux, Mac and Windows through the API provided from \n
* QSerialDevice by Denis Shienkov \c http://fireforge.net/projects/qserialdevice/ \c
*
* @class HardwareInterface hardwareinterface.hpp "hardwareinterface.hpp"
*/
class HardwareInterface : public QObject
{
Q_OBJECT
public:
    /**
    * @brief HardwareInterface Constructor
    *
    * @fn explicit HardwareInterface(QObject *parent = 0)
    * @param parent
    */
    explicit HardwareInterface(QObject *parent = 0);

    /**
    * @brief HardwareInterface Destructor
    *
    * @fn ~HardwareInterface()
    */
    ~HardwareInterface();

    /**
    * @brief Save a pointer to DataMemoryOrganisation
    *
    * @fn void setDataMemoryOrganisation(DataMemoryOrganisation *dmo)
    * @param dmo
    */
    void setDataMemoryOrganisation(DataMemoryOrganisation *dmo);

    /**
    * @brief Set the serial Port to the given Name
    * @details This will open the serial port with \b name \b and allows to write/read from it. \n
    * The signal \c stateChanged(bool) \c is emitted when the port can be opend otherwise \c error \c is emitted
    *
    * @fn void setSerialPort(QString name)
    * @param name
    */
    void setSerialPort(QString name);

    /**
    * @brief Stop the serial port
    *
    * @fn void stop()
    */
    void stop();

    /**
    * @brief Perform a synchronous query
    * @details This function will send out the data and wait until it receives some data \n
    * The function will wait at most 2 seconds for data.
    *
    * @fn void synchronousQuery()
    */
    void synchronousQuery();

private:
    /**
    * @brief The enum represents the states this interface can have
    *
    * @enum ReadState
    */
    enum ReadState { STARTREAD, ENDREAD };

    AbstractSerial *serialPort;
    DataMemoryOrganisation *dataMemoryOrganisation;
    AppSettings *settings;
    bool initialized;
    ReadState currentState;
    int readPortA;
    int readPortB;
    const char cr;
    QString readBuffer;
    QMutex *mutex;
    bool waitForAnswer;

signals:
    /**
    * @brief Signal emitted when data has been written to the port
    *
    * @fn void dataWritten(QString data)
    * @param data
    */
    void dataWritten(QString data);

    /**
    * @brief Signal emitted when data has been read from the port
    *
    * @fn void dataRead(QString data)
    * @param data
    */
    void dataRead(QString data);

    /**
    * @brief Signal emitted when an error occured while trying to open the port
    *
    * @fn void error(QString device, QString errorString)
    * @param device
    * @param errorString
    */
    void error(QString device, QString errorString);

    /**
    * @brief Signal emitted when the state of the HardwareInterface has changed
    *
    * @fn void stateChanged(bool state)
    * @param state
    */
    void stateChanged(bool state);

    /**
    * @brief Signal emitted when the HardwareInterface receives a MCLR
    *
    * @fn void mclrReceived()
    */
    void mclrReceived();

public slots:
    /**
    * @brief Send the data (TRIS, PORT) out the serial port
    *
    * @fn void processSerialPortQuery()
    */
    void processSerialPortQuery();

    /**
    * @brief Process the information which has been read from serial port
    *
    * @fn void processInformation()
    */
    void processInformation();

    /**
    * @brief Slot called when a ManualReceive has been triggered by the user
    *
    * @fn void onManualReceive(QString data)
    * @param data
    */
    void onManualReceive(QString data);

private slots:
    /**
    * @brief Slot called when readyRead is emitted from QSerialDevice
    *
    * @fn void readFromSerialPort()
    */
    void readFromSerialPort();

    /**
    * @brief Slot called when the DSR line from serial interface recognizes a change
    *
    * @fn void onDsrChanged(bool status)
    * @param status
    */
    void onDsrChanged(bool status);

};

#endif // HARDWAREINTERFACE_HPP
